// Input Controller
// (c) jimon game studio

#ifndef JEH_JEINPUTCONTROLLER
#define JEH_JEINPUTCONTROLLER

#include "jeUnknown.h"
#include "jeStl.h"
#include "IWindow.h"
#include "IMouseDriver.h"
#include "IKeyDriver.h"

namespace je
{
	namespace input
	{
		//! Input Controller Types
		enum InputControllerType
		{
			//! Key Controller
			ICT_KEY,

			//! Mouse Controller
			ICT_MOUSE,

			//! Text Controller
			ICT_TEXT
		};

		//! Input Controller
		class jeInputController:public je::jeUnknown
		{
		protected:
			//! Controller Type
			InputControllerType Type;

			//! Key Code
			KeyCode Code;

			//! Controller Position
			jeVector3df Position;

			//! Controller Speed
			jeVector3df Speed;

			//! Mouse Driver
			IMouseDriver * MouseDriver;

			//! Key Driver
			IKeyDriver * KeyDriver;

			//! Text
			jeStringd Text;

			//! Cursor Pos
			u32 CursorPos;

			//! Text Updated
			u1 TextUpdated;
		public:
			//! Constructor
			jeInputController(KeyCode code,IKeyDriver * keydriver)
				:Type(ICT_KEY),Code(code),MouseDriver(NULL),KeyDriver(keydriver),CursorPos(0),TextUpdated(false)
			{
				KeyDriver->Grab();
			}

			//! Constructor
			jeInputController(IMouseDriver * mousedriver)
				:Type(ICT_MOUSE),Code(KC_NULL),MouseDriver(mousedriver),KeyDriver(NULL),CursorPos(0),TextUpdated(false)
			{
				MouseDriver->Grab();
			}

			//! Constructor
			jeInputController(IKeyDriver * keydriver)
				:Type(ICT_TEXT),Code(KC_NULL),MouseDriver(NULL),KeyDriver(keydriver),CursorPos(0),TextUpdated(true)
			{
				KeyDriver->Grab();
			}


			//! Destructor
			~jeInputController()
			{
				if(KeyDriver)
					KeyDriver->Drop();

				if(MouseDriver)
					MouseDriver->Drop();
			}

			//! Return Controller Type
			InputControllerType GetType()
			{
				return Type;
			}

			//! Get Pressed Key Code
			KeyCode GetKey()
			{
				return Code;
			}

			//! Return true if button controller hit
			u1 IsHit()
			{
				return KeyDriver->IsHit(Code);
			}

			//! Return true if button down
			u1 IsDown(u1 Mode = false)
			{
				return KeyDriver->IsDown(Code,Mode);
			}

			//! Return true if button enable
			//! it's for button like caps lock, num lock and other
			u1 IsEnable()
			{
				return false;
			}

			//! Get Input Controller Position
			jeVector3df GetPosition()
			{
				jeVector2df NewPosT = MouseDriver->GetPosition();
				jeVector3df NewPos(NewPosT.X,NewPosT.Y,(f32)MouseDriver->GetMouseWheelPosition());
				Speed = NewPos - Position;
				Position = NewPos;

				return Position;
			}

			//! Get Input Controller Speed
			jeVector3df GetSpeed()
			{
				jeVector2df NewPosT = MouseDriver->GetPosition();
				jeVector3df NewPos(NewPosT.X,NewPosT.Y,(f32)MouseDriver->GetMouseWheelPosition());
				Speed = NewPos - Position;
				Position = NewPos;

				return Speed;
			}

			//! Set Input Controller Position 3d
			void SetPosition(const jeVector3df & NewPosition)
			{
				MouseDriver->SetPosition(jeVector2df(NewPosition.X,NewPosition.Y));
				Position = NewPosition;
			}

			//! Set Input Controller Position 2d
			void SetPosition(const jeVector2df & NewPosition)
			{
				MouseDriver->SetPosition(NewPosition);
				Position.SetXYZ(NewPosition.X,NewPosition.Y,Position.Z);
			}

			//! Check Inputed Text
			u1 CheckInputedText()
			{
				u1 IsUpdated = TextUpdated;
				TextUpdated = false;

				jeStringd InText = KeyDriver->GetTextStream();

				if(InText.GetSize()>0)
				{
					IsUpdated = true;

					jeStringd Text1 = Text.SubString(0,CursorPos);
					jeStringd Text2 = Text.SubString(CursorPos,Text.GetSize()-CursorPos+1);

					Text = Text1 + InText + Text2;

					CursorPos += InText.GetSize();
				}

				if(KeyDriver->IsDown(input::KC_RIGHT,true) && (CursorPos < Text.GetSize()))
				{
					IsUpdated = true;
					CursorPos++;
				}

				if(KeyDriver->IsDown(input::KC_LEFT,true) && (CursorPos > 0))
				{
					IsUpdated = true;
					CursorPos--;
				}

				if(KeyDriver->IsDown(input::KC_HOME,true))
				{
					IsUpdated = true;
					CursorPos = 0;
				}

				if(KeyDriver->IsDown(input::KC_END,true))
				{
					IsUpdated = true;
					CursorPos = Text.GetSize();
				}

				if(KeyDriver->IsDown(input::KC_DELETE,true) && (CursorPos < Text.GetSize()))
				{
					IsUpdated = true;
					Text.Erase(CursorPos);
				}

				if(InText.GetSize()>0)
				{
					for(u32 i=0;i<Text.GetSize();i++)
					{
						switch(Text[i])
						{
						case KC_BACK:
							{
								Text.Erase(i);
								if(i>0)
								{
									Text.Erase(i-1);
									i-=2;
									CursorPos-=2;
								}
								else
									CursorPos-=1;
								break;
							}
						case KC_RETURN:
							{
								Text.Erase(i);
								if(i > 0)
									i--;
							}
						default:
							break;
						}
					}
				}

				return IsUpdated;
			}

			//! Set Text
			void SetText(const jeStringd & NewText)
			{
				Text = NewText;
				TextUpdated = true;
			}

			//! Get Text
			const jeStringd & GetText()
			{
				return Text;
			}

			//! Get Cursor Pos
			u32 GetCursorPos()
			{
				return CursorPos;
			}

			//! Set Cursor Pos
			void SetCursorPos(u32 NewCursorPos)
			{
				CursorPos = NewCursorPos;
			}

			//! Flush Input Controller
			void Flush()
			{
				Position.Flush();
				Speed.Flush();

				Text = "";
				CursorPos = 0;
				TextUpdated = true;
			}
		};
	}
}

#endif
